/***********************************************************************************************************************
*                                                                                                                      *
* SPLASH build system v0.1                                                                                             *
*                                                                                                                      *
* Copyright (c) 2014 Andrew D. Zonenberg                                                                               *
* All rights reserved.                                                                                                 *
*                                                                                                                      *
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the     *
* following conditions are met:                                                                                        *
*                                                                                                                      *
*    * Redistributions of source code must retain the above copyright notice, this list of conditions, and the         *
*      following disclaimer.                                                                                           *
*                                                                                                                      *
*    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the       *
*      following disclaimer in the documentation and/or other materials provided with the distribution.                *
*                                                                                                                      *
*    * Neither the name of the author nor the names of any contributors may be used to endorse or promote products     *
*      derived from this software without specific prior written permission.                                           *
*                                                                                                                      *
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED   *
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL *
* THE AUTHORS BE HELD LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES        *
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR       *
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE       *
* POSSIBILITY OF SUCH DAMAGE.                                                                                          *
*                                                                                                                      *
***********************************************************************************************************************/

#include <string>
#include <map>
#include <vector>
#include "AntikernelModuleInfo.h"

using namespace std;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// AntikernelModuleInfo

AntikernelModuleInfo::AntikernelModuleInfo()
{

}

AntikernelModuleInfo::AntikernelModuleInfo(vector<string>& lines, unsigned int& i)
{
	//Module declaration
	char modname[128];
	if(1 != sscanf(lines[i].c_str(), "module %127s", modname))
		return;
	m_name = modname;
	
	//Read through lines
	for(; i < lines.size(); i++)
	{
		char keyword[32];
		if(1 != sscanf(lines[i].c_str(), "%31s", keyword))
			continue;
		string skeyword(keyword);
		
		//Load constants
		if(skeyword == "constant")
		{
			char name[128];
			unsigned int value;
			if(2 != sscanf(lines[i].c_str(), " constant %127s %d", name, &value))
				continue;
			m_constants[name] = pair<unsigned int, unsigned int>(8, value);
		}
		
		//Opcodes
		else if(skeyword == "opcode")
		{
			AntikernelOpcodeInfo info(lines, i, this);
			m_opcodeInfo[info.m_funcval] = info;
		}
		
		//Stop on the endmodule
		else if(skeyword == "endmodule")
			break;
			
		//Ignore anything else
	}
}

void AntikernelModuleInfo::SaveToFile(FILE* fp)
{	
	fprintf(fp, "module %s\n", m_name.c_str());
	for(auto const_it : m_constants)
		fprintf(fp, "    constant %s %d\n", const_it.first.c_str(), const_it.second.second);
	for(auto opcode_it : m_opcodeInfo)
		opcode_it.second.SaveToFile(fp);
	fprintf(fp, "endmodule\n");
	fprintf(fp, "\n");
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// AntikernelOpcodeInfo

AntikernelOpcodeInfo::AntikernelOpcodeInfo()
{
	
}

AntikernelOpcodeInfo::AntikernelOpcodeInfo(vector<string>& lines, unsigned int& i, AntikernelModuleInfo* parent)
{
	//Opcode declaration
	if(1 != sscanf(lines[i].c_str(), " opcode %u", &m_funcval))
		return;
	
	//Look up string opcode name
	string name;
	for(auto x : parent->m_constants)
	{
		if(x.second.second == m_funcval)
		{
			name = x.first;
			break;
		}
	}
	
	//Read through lines
	for(; i < lines.size(); i++)
	{
		char keyword[32];
		if(1 != sscanf(lines[i].c_str(), " %31s", keyword))
			continue;
		string skeyword(keyword);
		
		//Types
		if(skeyword == "type")
		{
			unsigned int type;
			if(1 != sscanf(lines[i].c_str(), " type %u", &type))
				continue;
			
			AntikernelPacketInfo info(lines, i);
			info.m_opname = name;
			m_packetInfo[type] = info;
		}
		
		//Stop on the end keyword
		else if(skeyword == "endopcode")
			break;
			
		//Ignore anything else
	}
}

void AntikernelOpcodeInfo::SaveToFile(FILE* fp)
{
	fprintf(fp, "    opcode %d\n", m_funcval);
	for(auto info_it : m_packetInfo)
	{
		fprintf(fp, "        type %d\n", info_it.first);
		info_it.second.SaveToFile(fp);
		fprintf(fp, "        endtype\n");
	}
	fprintf(fp, "    endopcode\n");
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// AntikernelPacketInfo

AntikernelPacketInfo::AntikernelPacketInfo()
{
	
}

AntikernelPacketInfo::AntikernelPacketInfo(vector<string>& lines, unsigned int& i)
{
	//Read through lines
	for(; i < lines.size(); i++)
	{
		char keyword[32];
		if(1 != sscanf(lines[i].c_str(), " %31s", keyword))
			continue;
		string skeyword(keyword);
		
		//Types
		if(skeyword == "var")
			m_varinfo.push_back(AntikernelPacketVariableInfo(lines, i));
		
		else if(skeyword == "desc")
		{
			char desc[512];
			if(1 != sscanf(lines[i].c_str(), " desc %511[^\n]", desc))
				continue;
			m_desc = desc;
		}
		
		//Stop on the end keyword
		else if(skeyword == "endtype")
			break;
			
		//Ignore anything else
	}
}

void AntikernelPacketInfo::SaveToFile(FILE* fp)
{
	fprintf(fp, "            desc %s\n", m_desc.c_str());
	for(auto var : m_varinfo)
		var.SaveToFile(fp);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// AntikernelPacketVariableInfo

AntikernelPacketVariableInfo::AntikernelPacketVariableInfo()
{
}

AntikernelPacketVariableInfo::AntikernelPacketVariableInfo(vector<string>& lines, unsigned int& i)
{
	//Read through lines
	for(; i < lines.size(); i++)
	{
		char keyword[32];
		if(1 != sscanf(lines[i].c_str(), " %31s", keyword))
			continue;
		string skeyword(keyword);
		
		//Types
		if(skeyword == "var")
		{
			char name[512];
			if(1 != sscanf(lines[i].c_str(), " var %511s", name))
				continue;
			m_displayName = name;
		}
		
		else if(skeyword == "desc")
		{
			char desc[512];
			if(1 != sscanf(lines[i].c_str(), " desc %511[^\n]", desc))
				continue;
			m_desc = desc;
		}
		
		else if(skeyword == "format")
		{
			char format[512];
			if(1 != sscanf(lines[i].c_str(), " format %511s", format))
				continue;
			string sformat(format);
			
			if(sformat == "dec")
				m_displayType = DISPLAY_TYPE_DEC;
			else if(sformat == "hex")
				m_displayType = DISPLAY_TYPE_HEX;
			else if(sformat == "nocaddr")
				m_displayType = DISPLAY_TYPE_NOCADDR;
			else if(sformat == "phyaddr")
				m_displayType = DISPLAY_TYPE_PHYADDR;
			else if(sformat == "mac")
				m_displayType = DISPLAY_TYPE_MACADDR;
			else if(sformat == "asciiz")
				m_displayType = DISPLAY_TYPE_ASCIIZ;
			else if(sformat == "ipv6")
				m_displayType = DISPLAY_TYPE_IPV6;
			else if(sformat == "enum")
				m_displayType = DISPLAY_TYPE_ENUM;
			else if(sformat == "fx8")
				m_displayType = DISPLAY_TYPE_FX8;
		}
		
		else if(skeyword == "data")
		{
			unsigned int word;
			unsigned int bithi;
			unsigned int bitlo;
			if(3 != sscanf(lines[i].c_str(), " data %u %u %u", &word, &bithi, &bitlo))
				continue;
			m_bitfields.push_back(AntikernelPacketBitfield(word, bithi, bitlo));
		}
		
		else if(skeyword == "enumval")
		{
			unsigned int value;
			char name[128];
			if(2 != sscanf(lines[i].c_str(), " enumval %u %127s", &value, name))
				continue;
			m_enumvals[value] = name;
		}
		
		//Stop on the end keyword
		else if(skeyword == "endvar")
			break;
			
		//Ignore anything else
	}
}

void AntikernelPacketVariableInfo::SaveToFile(FILE* fp)
{
	fprintf(fp, "            var %s\n", m_displayName.c_str());
	fprintf(fp, "                desc %s\n", m_desc.c_str());
	switch(m_displayType)
	{
	case DISPLAY_TYPE_DEC:
		fprintf(fp, "                format dec\n");
		break;
	case DISPLAY_TYPE_HEX:
		fprintf(fp, "                format hex\n");
		break;
	case DISPLAY_TYPE_NOCADDR:
		fprintf(fp, "                format nocaddr\n");
		break;
	case DISPLAY_TYPE_PHYADDR:
		fprintf(fp, "                format phyaddr\n");
		break;
	case DISPLAY_TYPE_MACADDR:
		fprintf(fp, "                format mac\n");
		break;
	case DISPLAY_TYPE_ASCIIZ:
		fprintf(fp, "                format asciiz\n");
		break;
	case DISPLAY_TYPE_IPV6:
		fprintf(fp, "                format ipv6\n");
		break;
	case DISPLAY_TYPE_ENUM:
		fprintf(fp, "                format enum\n");
		break;
	case DISPLAY_TYPE_FX8:
		fprintf(fp, "                format fx8\n");
		break;
	default:
		break;
	}
	for(auto x : m_bitfields)
		fprintf(fp, "                data %d %d %d\n", x.m_word, x.m_bithi, x.m_bitlo);
	for(auto x : m_enumvals)
		fprintf(fp, "                enumval %u %s\n", x.first, x.second.c_str());
	fprintf(fp, "            endvar\n");
}
